
"""对发票的改造"""

from odoo import models, api, fields
from odoo.tools import float_compare
from odoo.exceptions import UserError, ValidationError


class Invoice(models.Model):
    _inherit = 'account.invoice'

    nd_type = fields.Selection([
        ('normal', '普票'),
        ('vat', '增票'),
        ('electronic', '电子发票')
        ], string="发票类型", copy=False)
    nd_invoice_num = fields.Char('发票号')

    # 开票信息
    nd_invoice_info_id = fields.Many2one('nd.invoice.info', string="开票信息")
    nd_invoice_name = fields.Char('开票公司', compute="_compute_nd_invoice_info", store=True)
    nd_vat_number = fields.Char('增值税号', compute="_compute_nd_invoice_info", store=True)
    nd_opening_bank = fields.Char('开户行', compute="_compute_nd_invoice_info", store=True)
    nd_reg_address = fields.Char('税务注册地址', compute="_compute_nd_invoice_info", store=True)
    nd_phone = fields.Char('开票电话', compute="_compute_nd_invoice_info", store=True)
    nd_zip_code = fields.Char('邮编', compute="_compute_nd_invoice_info", store=True)
    nd_bank_account = fields.Char('银行账户', compute="_compute_nd_invoice_info", store=True)
    nd_department_id = fields.Many2one('hr.department', string="部门")
    nd_hospital_department = fields.Char('科室')

    @api.depends('nd_invoice_info_id')
    def _compute_nd_invoice_info(self):
        for order in self:
            if order.nd_invoice_info_id:
                order.update({
                    'nd_invoice_name': order.nd_invoice_info_id.name,
                    'nd_vat_number': order.nd_invoice_info_id.vat_number,
                    'nd_opening_bank': order.nd_invoice_info_id.opening_bank,
                    'nd_reg_address': order.nd_invoice_info_id.tax_registration_address,
                    'nd_phone': order.nd_invoice_info_id.phone,
                    'nd_zip_code': order.nd_invoice_info_id.zip_code,
                    'nd_bank_account': order.nd_invoice_info_id.customer_invoice_bank_account,
                })


    @api.model
    def nd_generate_account_invoice(self, wizard_id):
        """
        根据传入的预发票明细pre_invoice_lines，生成发票
        :param wizard_id: nd.invoice.wizard, 开票向导
        :return:
        """
        if not wizard_id:
            return

        pre_invoice_lines = wizard_id.line_ids.mapped('invoice_line')
        wizard_lines = wizard_id.line_ids
        # pre_invoice_lines = self.env['pre.account.line'].browse(pre_invoice_line_ids)

        self._nd_check_pre_invoice_lines(pre_invoice_lines)

        origin_list = []
        for line in pre_invoice_lines:
            if line.sale_line_id:
                origin_list.append(line.sale_line_id.order_id.name)
            if line.purchase_line_id:
                origin_list.append(line.purchase_line_id.order_id.name)
        origin = ','.join(set(origin_list))

        partner_id, invoice_type, date_invoice, user_id, department_id, nd_hospital_department = self._nd_prepare_info_invoice(pre_invoice_lines)
        invoice_id = self.env['account.invoice'].create({
            'type': invoice_type,
            'partner_id': partner_id.id,
            # 'date_invoice': date_invoice,
            'date_invoice': False,
            'origin': origin,
            'user_id': user_id.id,
            'nd_department_id': department_id.id,
            'nd_hospital_department': nd_hospital_department,
        })

        line_datas = []
        for wizard_line in wizard_lines:
            line = wizard_line.invoice_line
            if line.type not in ['sale', 'purchase']:
                continue
            line.write({'state': wizard_line.finish and 'invoiced' or 'partly'})    # 更新发票明细状态
            price_unit, account = self._nd_prepare_invoice_line_info(wizard_line, invoice_type, invoice_id.fiscal_position_id, invoice_id.company_id)
            data = {
                'pre_account_line_id': line.id,
                'product_id': line.product_id.id,
                'name': line.product_id.display_name,
                'quantity': wizard_line.invoice_qty,
                'uom_id': line.measure_unit.id,
                'product_uom': line.measure_unit.id,
                'price_unit': price_unit,
                'account_id': account.id,
                'invoice_line_tax_ids': [(6, 0, line.tax_id.ids)],
            }
            # 和业务单据明细关联，TODO 这里如何确保肯定有对应的业务明细单据？？
            if line.type == 'sale':
                data['sale_line_ids'] = [(4, line.sale_line_id.id or line.move_id.sale_line_id.id)]
            elif line.type == 'purchase':
                data['purchase_line_id'] = line.purchase_line_id.id or line.move_id.purchase_line_id.id
            else:
                raise UserError(f'不能识别的发票明细类型：{line.type}')

            line_datas.append((0, 0, data))

        invoice_id.write({'invoice_line_ids': line_datas})
        return invoice_id

    def _nd_generate_hongchong_invoice(self, wizard_id):
        """生成红冲发票"""
        if not wizard_id:
            return

        invoice_ids = wizard_id.invoice_ids
        # 整张红冲需要指定发票
        if wizard_id.hc_option == 'all' and not invoice_ids:
            raise UserError('请选择要整张红冲的发票')

        # 部分红冲无需手动指定发票，但是为了兼容系统原生功能，仍需要相关发票，现在直接从关联发票里任选一张
        if wizard_id.hc_option == 'part':
            invoice_ids = self.env['nd.invoice.wizard']._get_invoices_relative(wizard_id.line_ids.mapped('invoice_line'))
            if not invoice_ids:
                raise UserError('所选发票明细没有对应的发票，无法红冲！')
            invoice_ids = invoice_ids[0]

        refund_wizard = self.env['account.invoice.refund'].with_context(active_id=invoice_ids[0].id, active_ids=invoice_ids.ids).new({
                            'description': wizard_id.reason, 'filter_refund': 'refund'})
        action = refund_wizard.with_context(nd_wizard_lines=wizard_id.line_ids.ids).compute_refund('refund')

        # 整张红冲时，需要更新被红冲发票的状态
        if wizard_id.hc_option == 'all':
            invoice_ids.write({'nd_total_hc': True})

            # 如果这里引发红冲是退货,则不更改原发票明细状态.如果不是退货引起的,则更改原发票明细状态,允许其重新再开票
            # if not wizard_id.mapped('line_ids')[0].invoice_line.move_id.origin_returned_move_id:
            #     invoice_ids.mapped('invoice_line_ids.pre_account_line_id')._update_state()

        # 更新红冲发票明细的状态
        for wizard_line in wizard_id.mapped('line_ids'):
            wizard_line.invoice_line.update({'state': wizard_line.finish and 'invoiced' or 'partly'})

        invoice_id = False
        if action['res_id'] > 0:
            invoice_id = [action['res_id']]
        if not invoice_id:
            for d in action['domain']:
                if d[0] == 'id' and d[1] == 'in':
                    invoice_id = d[2]
                    break

        refund_invoice = self.env['account.invoice'].browse(invoice_id)
        # 要单独记录红冲发票和红冲明细之间的关联，在取消红冲发票时使用
        refund_invoice.write({'pre_hc_account_line_ids': [(6, 0, wizard_id.mapped('line_ids.invoice_line').ids)]})
        return refund_invoice

    @api.model
    def _prepare_refund(self, invoice, date_invoice=None, date=None, description=None, journal_id=None):
        """修改红冲发票的金额，系统默认的应该是全额？"""
        values = super()._prepare_refund(invoice, date_invoice, date, description, journal_id)
        wizard_lines = self.env.context.get('nd_wizard_lines')
        # 有wizard_lines认为是从退货红票上发起的红冲，发票明细则由红冲逻辑来恢复到可开票状态。
        # 没有wizard_lines则认为是从发票上直接发起的红票请求，这里就要将发票明细恢复到可开票状态
        if wizard_lines:
            wizard_lines = self.env['nd.invoice.wizard.line'].browse(wizard_lines)
            wizard_id = wizard_lines.mapped('wizard_id')
            # 这里假定所有的wizard_lines都是红冲明细
            if wizard_id.hc_option == 'part':
                line_datas = []
                invoice_type = values['type']
                fpos = self.env['account.fiscal.position'].browse(values['fiscal_position_id'])
                company_id = self.env['res.company'].browse(values['company_id'])

                for wizard_line in wizard_lines:
                    line = wizard_line.invoice_line
                    if line.type not in ['sale_return', 'purchase_return']:
                        continue
                    line.write({'state': wizard_line.finish and 'invoiced' or 'partly'})    # 更新发票明细状态

                    price_unit, account = self._nd_prepare_invoice_line_info(wizard_line, invoice_type, fpos, company_id)
                    data = {
                        'pre_account_line_id': line.id,
                        'product_id': line.product_id.id,
                        'name': line.product_id.display_name,
                        'quantity': wizard_line.invoice_qty,
                        'uom_id': line.measure_unit.id,
                        'product_uom': line.measure_unit.id,
                        'price_unit': price_unit,
                        'account_id': account.id,
                        'invoice_line_tax_ids': [(6, 0, line.tax_id.ids)],
                    }
                    # 和业务单据明细关联，
                    if line.type == 'sale_return':
                        data['sale_line_ids'] = [(4, line.sale_line_id.id or line.move_id.sale_line_id.id)]
                    elif line.type == 'purchase_return':
                        data['purchase_line_id'] = line.purchase_line_id.id or line.move_id.purchase_line_id.id
                    else:
                        raise UserError(f'不能识别的红冲发票明细类型：{line.type}')

                    line_datas.append((0, 0, data))

                if line_datas:
                    values['invoice_line_ids'] = line_datas
            elif wizard_id.hc_option == 'all':
                # 整张红冲.要改每一行的pre_account_line_id.但是红冲明细数量和发票明细数量可能不一致
                # 所以让每一张发票的明细都关联到第一张红冲明细
                pass
                # for line in values['invoice_line_ids']:
                #     line[2]['pre_account_line_id'] = wizard_lines[0].invoice_line.id
        else:
            invoice.mapped('invoice_line_ids.pre_account_line_id').write({"state": "partly"})
        return values

    def _nd_prepare_info_invoice(self, pre_invoice_lines):
        """获取主表account.invoice的信息"""
        partner_id = pre_invoice_lines[0].partner_id
        invoice_type = pre_invoice_lines[0].type
        user_id = self.env['res.users']
        department_id = self.env['hr.department']
        nd_hospital_department = ''

        if invoice_type == 'sale':
            type = 'out_invoice'
            sale_id = pre_invoice_lines[0].sale_line_id.order_id
            user_id = sale_id.user_id
            department_id = sale_id.nd_department_id
            nd_hospital_department = sale_id.nd_hospital_department
        elif invoice_type == 'purchase':
            type = 'in_invoice'
            purchase_id = pre_invoice_lines[0].purchase_line_id.order_id
            user_id = purchase_id.user_id
            department_id = user_id.employee_ids[0].department_id
        else:
            raise UserError(f'不能识别的发票明细类型：{invoice_type}')

        date_invoice = fields.Date.today()
        return partner_id, type, date_invoice, user_id, department_id, nd_hospital_department

    def _nd_prepare_invoice_line_info(self, wizard_line, invoice_type, fpos, company_id):
        """获取待创建发票明细行account.invoice所需要的信息，这里的金额来自于向导明细上填写的"""

        account = self.env['account.invoice.line'].get_invoice_line_account(invoice_type,
                                                                            wizard_line.invoice_line.product_id,
                                                                            fpos,
                                                                            company_id)

        try:
            price_unit = wizard_line.invoice_amount / wizard_line.invoice_qty
        except ZeroDivisionError:
            price_unit = 0
        return price_unit, account

    def _nd_check_pre_invoice_lines(self, pre_invoice_lines):
        """检查传入的pre_invoice_lines是否可以生成发票，
        主要检查开票状态，是否同一客户、供应商，发票类型等
        TODO 暂时还没有限制同一销售员"""

        if len(pre_invoice_lines.mapped('partner_id')) > 1:
            raise UserError('所选发票明细不属于同一客户或者供应商！')

        if pre_invoice_lines.filtered(lambda x: x.state in ['invoiced', 'free', 'hc']):
            raise UserError('部分所选明细已经完成开票或者无需开票，不能再次勾选开票！')

        types = set(pre_invoice_lines.mapped('type'))
        if not (len(types) == 1 or types == {'sale', 'sale_return'} or types == {'purchase', 'purchase_return'}):
            raise UserError('所选发票明细不属于同一类型！')

# 发票编号代码，具体规则：
#         销售普票：XSPTFP + 年份 + 月份 + 4位编号
#         销售专票：XSZYFP + ...
#         销售电子发票：XSDZFP + ...
#         采购普票：CGPTFP + ...
#         采购专票：CGZYFP + ...
#         采购电子发票：CGDZFP + ...
SEQ_TYPE = {
    'sale': {
        'normal': 'nd.account.sale.normal',
        'vat': 'nd.account.sale.vat',
        'electronic': 'nd.account.sale.ele'
    },
    'purchase': {
        'normal': 'nd.account.purchase.normal',
        'vat': 'nd.account.purchase.vat',
        'electronic': 'nd.account.purchase.ele'
    }
}


class AccountMove(models.Model):
    _inherit = 'account.move'

    @api.multi
    def post(self, invoice=False):
        """改动发票的编号
        """
        if invoice:
            if invoice.type in ['out_invoice', 'in_refund']:
                seq_code = SEQ_TYPE['sale'].get(invoice.nd_type)
            elif invoice.type in ['in_invoice', 'out_refund']:
                seq_code = SEQ_TYPE['purchase'].get(invoice.nd_type)
            else:
                seq_code = False
            for move in self:
                if seq_code:
                    move.name = self.env['ir.sequence'].next_by_code(seq_code)
        return super().post(invoice=invoice)
